#pragma once
#include <iostream>
#include <string>
#include "socket.hpp"
#include "log.hpp"
#include "nocopy.hpp"
#include "epoller.hpp"
#include "setNonBlock.hpp"

extern Log lg;

class epollServer : public nocopy
{
    static const int MAX_EVENTS_NUM = 1024;

public:
    epollServer(const uint16_t port)
        : _port(port) {}
    bool init()
    {
        _listenSock.bind(_port);
        if (_listenSock.listen() == -1)
        {
            lg(Error, "server socket listen fail!");
            exit(2);
        }
        // 设置套接字能重复使用
        _listenSock.set_no_time_wait();
        return true;
    }
    void start()
    {
        // 1. 将listenSock放到epoller模型中, 等待下次通知
        _epollModel.epollFileUpdate(EPOLL_CTL_ADD, _listenSock.fd(), EVENT_IN);
        setNonBlock(_listenSock.fd());
        while (true)
        {
            // 设置成-1表示阻塞等待
            int n = _epollModel.epollWait(_Events, MAX_EVENTS_NUM, -1);
            if (n > 0)
                dispatcher(n);
            else if (n == 0)
                lg(Info, "timeout , no event create...");
            else
                lg(Warning, "epoll wait error!");
        }
    }

private:
    // 链接管理器
    void accepter()
    {
        while (true)
        {
            std::string srcIp;
            uint16_t srcPort;
            int fd = _listenSock.accept(&srcIp, &srcPort);
            if (fd == -1)
            {
                if (errno == EAGAIN)
                    break;
                else
                    lg(Warning, "socket accept fail!");
            }
            else // 此时需要将心发现的文件描述幅符加入epoller
            {
                lg(Info, "get a new link, fd:%d", fd);
                _epollModel.epollFileUpdate(EPOLL_CTL_ADD, fd, EVENT_IN);
            }
        }
    }
    void eventHandler(int fd)
    {
        // 处理当前的fd读事件
        char inbuffer[1024];
        ssize_t n = read(fd, inbuffer, sizeof inbuffer - 1);
        if (n > 0)
        {
            inbuffer[n] = 0;
            std::string ret = std::string("server echo $ ") + inbuffer;
            write(fd, ret.c_str(), ret.size());
        }
        else if (n == 0)
        {
            lg(Info, "client close the link, fd: %d", fd);
            // 如果fd关闭了，那就没有必要继续关心当前文件描述符了
            _epollModel.epollFileDel(fd);
            close(fd);
        }
        else
        {
            lg(Warning, "server read fail, fd: %d", fd);
            // 出错了也不用继续关心了
            _epollModel.epollFileDel(fd);
            close(fd);
        }
    }
    // 事件派发器
    void dispatcher(int num)
    {
        for (int i = 0; i < num; ++i)
        {
            int fd = _Events[i].data.fd;
            uint32_t flag = _Events[i].events;
            if (flag & EVENT_IN)
                if (fd == _listenSock.fd())
                    accepter();
                else
                    eventHandler(fd);
        }
    }

private:
    uint16_t _port;
    // epoll 服务器一定要有一个epoll模型
    epoller _epollModel;
    tcp_socket _listenSock;
    struct epoll_event _Events[MAX_EVENTS_NUM];
};